//
// Created by Martin on 2023/4/12.
//

#include "ftpserver/OsFile.h"

#include <thread>
#include <vector>

using namespace std;
using namespace ftp::filesystem;

// read file with sharing
static void thread_func_read(const char* path, int id)
{
    bool res(false);
    res = OSFileInstance.allocFile(path, kFileStatusRead);
    if (res) {
        printf("thread id%d: success to read lock file with(%s)\n", id, path);
    }
    else {
        printf("thread id%d: fail to read lock file(%s)\n", id, path);
    }
    // to avoid free lock at once
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    OSFileInstance.freeFile(path);
}

// write file
static void thread_func_write(const char* path, int id)
{
    bool res(false);
    res = OSFileInstance.allocFile(path, kFileStatusWrite);
    if (res) {
        printf("thread id%d: success to write lock file(%s)\n", id, path);
    }
    else {
        printf("thread id%d: fail to write lock file(%s)\n", id, path);
    }
    // to avoid free lock at once
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    OSFileInstance.freeFile(path);
}

// read-write file
static void thread_func_readwrite(const char* path, int id)
{
    bool res(false);
    if (id % 2 == 0) { // even num
        res = OSFileInstance.allocFile(path, kFileStatusRead);
        if (res) {
            printf("thread id%d: success to read lock file with(%s)\n", id, path);
        }
        else {
            printf("thread id%d: fail to read lock file(%s)\n", id, path);
        }
        // to avoid free lock at once
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        OSFileInstance.freeFile(path);
    }
    else {
        res = OSFileInstance.allocFile(path, kFileStatusWrite);
        if (res) {
            printf("thread id%d: success to write lock file(%s)\n", id, path);
        }
        else {
            printf("thread id%d: fail to write lock file(%s)\n", id, path);
        }
        // to avoid free lock at once
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        OSFileInstance.freeFile(path);
    }
}

// write-read file
static void thread_func_writeread(const char* path, int id)
{
    bool res(false);
    if (id % 2 == 0) { // even num
        res = OSFileInstance.allocFile(path, kFileStatusWrite);
        if (res) {
            printf("thread id%d: success to write lock file(%s)\n", id, path);
        }
        else {
            printf("thread id%d: fail to write lock file(%s)\n", id, path);
        }
        // to avoid free lock at once
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        OSFileInstance.freeFile(path);
    }
    else {
        res = OSFileInstance.allocFile(path, kFileStatusRead);
        if (res) {
            printf("thread id%d: success to read lock file with(%s)\n", id, path);
        }
        else {
            printf("thread id%d: fail to read lock file(%s)\n", id, path);
        }
        // to avoid free lock at once
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        OSFileInstance.freeFile(path);
    }
}

int main(int argc, char* argv[])
{
    bool res;

    { // only one thread read file
        printf("test case: read lock file\n");

        const char* file = "";
        res = OSFileInstance.allocFile(file, kFileStatusRead);
        if (res) {
            printf("success to read file(%s)\n", file);
        }
        else {
            printf("fail to read file(%s)\n", file);
        }
        OSFileInstance.freeFile(file);
    }

    { // read file
        printf("\ntest case: read lock file with sharing\n");

        std::vector<std::thread> threads;
        constexpr size_t numThreads = 10;
        const char* file = "/dev/null";

        for (size_t i = 0; i < numThreads; ++i) {
            threads.emplace_back(thread_func_read, file, i + 1);
        }

        for (size_t i = 0; i < numThreads; ++i) {
            threads[i].join();
        }
    }

    { // write file
        printf("\ntest case: write lock file\n");

        std::vector<std::thread> threads;
        constexpr size_t numThreads = 10;
        const char* file = "/dev/null";

        for (size_t i = 0; i < numThreads; ++i) {
            threads.emplace_back(thread_func_write, file, i + 1);
        }
        for (size_t i = 0; i < numThreads; ++i) {
            threads[i].join();
        }
    }

    { // read and write file
        printf("\ntest case: read-write lock file\n");

        std::vector<std::thread> threads;
        constexpr size_t numThreads = 10;
        const char* file = "/dev/null";

        for (size_t i = 0; i < numThreads; ++i) {
            threads.emplace_back(thread_func_readwrite, file, i + 1);
        }
        for (size_t i = 0; i < numThreads; ++i) {
            threads[i].join();
        }
    }

    { // write and read file
        printf("\ntest case: write-read lock file\n");

        std::vector<std::thread> threads;
        constexpr size_t numThreads = 10;
        const char* file = "/dev/null";

        for (size_t i = 0; i < numThreads; ++i) {
            threads.emplace_back(thread_func_writeread, file, i + 1);
        }
        for (size_t i = 0; i < numThreads; ++i) {
            threads[i].join();
        }
    }

    return 0;
}
